/***************************************************/
// SCRambl - Written in Visual Studio 2010
// Language: C/C++
// System: Win32
/****************************************************/
#include "stdafx.h"
#include "CScrLanguage.h"
#include "CTokens.h"

TODO("Create wrappers for WinAPI and low-level C functions for future portability ease");

FILE * g_hInputFile;
FILE * g_hOutputFile;
char g_szFileInName[MAX_PATH];
char g_szFileOutName[MAX_PATH];
char g_szFileBuffer[2048];
char g_szTempString[4096];
char g_szErrorString[2048];
char g_szOutputBuffer[512];
DWORD g_dwVersion;
std::string g_sTempString;
int g_nErrorStackCount = 0;

CSCRambl				SCRambl;
CTokens					Tokens;

#ifdef DEBUGIT
	char DEBUG_STRING[4096];
#endif

void CSCRambl::LoadDefinitions()
{
	CSyntax::LoadTypes("types.def");
	CConstants::LoadFromFile("constants.def");
	CCommands::LoadFromFile("commands.def");
	COperators::LoadFromFile("operators.def");
	CSyntax::LoadScrInfo("scr.def");
	CSyntax::LoadScmInfo("scm.def");
	CLimits::LoadFromFile("limits.def");
	CVariables::LoadFromFile("variables.def");
	CLabels::LoadFromFile("labels.def");
}
void CSCRambl::LoadGameFiles()
{
	if(*SCRambl.Config.Installs.GetGameInstall()->m_szDatFilePath)
		CConstants::LoadDatFile(SCRambl.Config.Installs.GetGameInstall()->m_szDatFilePath);
	else
		Print("Unable to load DAT file");

	if(*SCRambl.Config.Installs.GetGameInstall()->m_szIdeFilePath)
		CConstants::LoadIdeFile(SCRambl.Config.Installs.GetGameInstall()->m_szIdeFilePath);
	else
		Print("Unable to load IDE file");

	if(SCRambl.Config.Installs.GetGameInstall()->m_bHavePedFilePath)
		CConstants::LoadIdeFile(SCRambl.Config.Installs.GetGameInstall()->m_szPedFilePath);
	if(SCRambl.Config.Installs.GetGameInstall()->m_bHaveVehFilePath)
		CConstants::LoadIdeFile(SCRambl.Config.Installs.GetGameInstall()->m_szVehFilePath);
}
	
inline void		*	AlignAddress(size_t pAddress, size_t alignment)
{
	return (void*)((pAddress+alignment-1) & ~(alignment-1));
}
__int64				JoinInt32(int high, int low)
{
	ULARGE_INTEGER li;
	li.HighPart = high;
	li.LowPart = low;
	return li.QuadPart;
}
__int64				GetTime()
{
	SYSTEMTIME st;
	FILETIME ft;
	GetSystemTime(&st);
	SystemTimeToFileTime(&st, &ft);
	return JoinInt32(ft.dwHighDateTime, ft.dwLowDateTime);
}
__int64				GetFileModifiedTime(FILE *hFile)
{
	BY_HANDLE_FILE_INFORMATION fi;
	GetFileInformationByHandle(hFile, &fi);
	return JoinInt32(fi.ftLastWriteTime.dwHighDateTime, fi.ftLastWriteTime.dwLowDateTime);
}
__int64				GetFileModifiedTime(const char *szPath)
{
	WIN32_FILE_ATTRIBUTE_DATA fi;
	GetFileAttributesEx(szPath, GetFileExInfoStandard, &fi);
	return JoinInt32(fi.ftLastWriteTime.dwHighDateTime, fi.ftLastWriteTime.dwLowDateTime);
}
__int64				FiletimeToInt64(const FILETIME &ft)
{
	return JoinInt32(ft.dwHighDateTime, ft.dwLowDateTime);
}
bool				DoesDirExist(const char *szDirPath)
{
	DWORD dwAttr = GetFileAttributes(szDirPath);
	return dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_DIRECTORY);
}
void				GetFileDirectory(const char *szFilePath, char *szDirectory, int nSize)
{
	const char	* pStr1 = strrchr(szFilePath, '\\');
	if(!pStr1) *szDirectory = '\0';
	else
	{
		if(!nSize || (nSize > pStr1 - szFilePath)) nSize = pStr1 - szFilePath;
		szncpy(szDirectory, szFilePath, nSize+1);
	}
}
inline FILE		*	OpenExecutable(const char * szCmd, const char * szMode)
{
	return _popen(szCmd, szMode);
}
inline FILE		*	OpenFile(const char *szFilePath, const char *szOpenMode)
{
	return fopen(szFilePath, szOpenMode);
}
inline int			CloseExecutable(FILE *hFile)
{
	return _pclose(hFile);
}
inline void			CloseFile(FILE *hFile)
{
	fclose(hFile);
}
inline DWORD		FileGetSize(FILE *hFile)
{
	DWORD p = FileGetCursorPos(hFile);
	SeekFile(hFile, 0, SEEK_END);
	DWORD e = FileGetCursorPos(hFile);
	SeekFile(hFile, p, SEEK_SET);
	return e;
}
inline int			FileGetCursorPos(FILE *hFile)
{
	return ftell(hFile);
}
inline bool			IsEndOfFile(FILE *hFile)
{
	return ferror(hFile) || feof(hFile);
}
int					SeekFile(FILE *hFile, int offset, int origin=SEEK_SET)
{
	return fseek(hFile, offset, origin);
}
char			*	GetConfigLine(FILE *hFile)
{
	while(fgets(g_szFileBuffer, sizeof(g_szFileBuffer), hFile))
	{
		char *c = g_szFileBuffer;
		while(IsSpace(*c)) c++;

		switch(*c)
		{
		case '\0':
		case '\n':
		case '\r':
		case ';':
			break;
		default:
			return strtok(c, ";\n\r");
		}
	}
	return NULL;
}
bool				WriteStringToFile(FILE *hFile, const char *str)
{
	return fputs(str, hFile) >= 0;
}
bool				ReadStringFromFile(FILE *hFile, char *str, int len)
{
	return fgets(str, len, hFile) == str;
}
size_t				ReadBytesFromFile(FILE *hFile, void *buff, int len)
{
	return fread(buff, sizeof(char), len, hFile);
}
size_t				WriteBytesToFile(FILE *hFile, const void *buff, int len)
{
	return fwrite(buff, sizeof(char), len, hFile);
}

template<typename T>
T ReadFromFile(FILE *hFile)
{
	T val;
	fread(&val, sizeof(T), 1, hFile);
	return val;
}
template float	ReadFromFile<float>(FILE *hFile);
template DWORD	ReadFromFile<DWORD>(FILE *hFile);
template int	ReadFromFile<int>(FILE *hFile);
template WORD	ReadFromFile<WORD>(FILE *hFile);
template short	ReadFromFile<short>(FILE *hFile);
template BYTE	ReadFromFile<BYTE>(FILE *hFile);
template char	ReadFromFile<char>(FILE *hFile);

template<typename T>
size_t WriteToFile(FILE *hFile, T val)
{
	fwrite(&val, sizeof(val), 1, hFile);
	return sizeof(val);
}

template size_t WriteToFile<float>(FILE *hFile, float val);
template size_t WriteToFile<DWORD>(FILE *hFile, DWORD val);
template size_t WriteToFile<int>(FILE *hFile, int val);
template size_t WriteToFile<WORD>(FILE *hFile, WORD val);
template size_t WriteToFile<short>(FILE *hFile, short val);
template size_t WriteToFile<BYTE>(FILE *hFile, BYTE val);
template size_t WriteToFile<char>(FILE *hFile, char val);

char tostr_buf[25];

static const char hexchars[] = "0123456789ABCDEF";

template<typename T>
const char * tohexstr(T v, char *out, int bytes)
{
	char * str = out ? out : tostr_buf;
	do
	{
		T m = v % 16;
		/*if(m || !v)
		{*/
			*str++ = hexchars[m];
			if(bytes) bytes--;
		//}
	}
	while((v /= 16) || bytes);
	*str = '\0';
	ReverseString(tostr_buf, str - 1);
	while(bytes-- > 0) *str++ = '0';
	*str = '\0';
	return tostr_buf;
}
template const char * tohexstr<DWORD>(DWORD v, char *out, int bytes);
template const char * tohexstr<WORD>(WORD v, char *out, int bytes);
template const char * tohexstr<BYTE>(BYTE v, char *out, int bytes);
template const char * tohexstr<int>(int v, char *out, int bytes);
template const char * tohexstr<short>(short v, char *out, int bytes);
template const char * tohexstr<char>(char v, char *out, int bytes);

template<typename T>
const char * tostr(const T& v)
{
	modp_itoa10(v, tostr_buf);
	return tostr_buf;
}
template const char * tostr<int>(const int& v);
template const char * tostr<short>(const short& v);
template const char * tostr<char>(const char& v);

/*template<typename T>
string tostring(const T& v)
{
	//modp_dtoa2(v, tostr_buf, 4); // bad precision, bad decision

	// not so pretty... + slower than tostr()
	string strn = str(boost::format("%d") % v);
	if(strn.find('.') == strn.npos) strn += ".0";
	return strn;
}/*/

template string tostring<float>(const float& v);

template<> const char * tostr<float>(const float& v)
{
	// All hail sprintf? :o
	sprintf(tostr_buf, "%g", v);
	char * str = tostr_buf;
	while(*++str != '.')
	{
		if(!*str)
		{
			*str++ = '.';
			*str++ = '0';
			*str++ = '\0';
			break;
		}
	}
	return tostr_buf;
}
template<> const char * tostr<DWORD>(const DWORD& v)
{
	modp_uitoa10(v, tostr_buf);
	return tostr_buf;
}
template<> const char * tostr<WORD>(const WORD& v)
{
	modp_uitoa10(v, tostr_buf);
	return tostr_buf;
}
template<> const char * tostr<BYTE>(const BYTE& v)
{
	modp_uitoa10(v, tostr_buf);
	return tostr_buf;
}

int NumMatchingBits(DWORD a, DWORD b)
{
	DWORD mask = a & (a >> 1) & 0x55555555;;
	b &= mask | (mask << 1);
	b = (b & 0x33333333) + ((b & 0xCCCCCCCC) >> 2);
	b = (b & 0x0F0F0F0F) + ((b & 0xF0F0F0F0) >> 4);
	b = (b & 0x00FF00FF) + ((b & 0xFF00FF00) >> 8);
	b = (b & 0x0000FFFF) + ((b & 0xFFFF0000) >> 16);
	return b;
}
int GetIntNumBytes(int nVal)
{
	int v = abs(nVal);
	if(v < 0x80) return 1;
	if(v < 0x8000) return 2;
	return 4;
}
int GetDWordNumBytes(DWORD dwVal)
{
	if(dwVal <= 0xFF) return 1;
	if(dwVal <= 0xFFFF) return 2;
	return 4;
}

inline int IsAlphabetic(char c){return isalpha((int)c);}
inline int IsAlphanumeric(char c){return isalnum((int)c);}
inline int IsDecimal(char c){return isdigit((int)c);}
inline int IsHex(char c){return isxdigit((int)c);}
inline int IsSpace(char c){return isspace((int)c);}
inline int	IsPrint(char c){return isprint((int)c);}
inline int IsUpper(char c){return isupper((int)c);}
inline int IsLower(char c){return islower((int)c);}

inline bool		IsIdentifierChar(char c)
{
	return IsAlphanumeric(c) || c == '_';
}
bool			IsStringInt(const char *szStr)
{
	if(*szStr == '-') szStr++;
	if(*szStr == '0' && ToLower(szStr[1]) == 'x')
	{
		for(szStr += 2; *szStr; szStr++)
		{
			if(IsSpace(*szStr)) return true;
			else if(!IsHex(*szStr)) return false;
		}
	}
	else if(IsDecimal(*szStr))
	{
		for(szStr++; *szStr; szStr++)
		{
			if(IsSpace(*szStr)) return true;
			else if(!IsDecimal(*szStr)) return false;
		}
	}
	else return false;
	return true;
}
bool			IsStringFloat(const char *szStr)
{
	bool b = 0;
	if(*szStr == '-') szStr++;
	if(IsDecimal(*szStr))
	{
		for(szStr++; *szStr; szStr++)
		{
			if(*szStr == '.')
			{
				if(b) return false;
				b = 1;
			}
			if(!IsDecimal(*szStr) && *szStr != '.') break;
		}
	}
	else return false;
	if(!b && (*szStr == 'f' || *szStr == 'F')) b = 1;
	return b;
}
bool			IsStringDecimal(const char *szStr, int nLimit)
{
	if(!nLimit) nLimit = strlen(szStr);
	for(; (nLimit)--; szStr++) if(!IsDecimal(*szStr)) break;
	return nLimit <= 0;
}
bool			IsStringHex(const char *szStr, int nLimit)
{
	if(!nLimit) nLimit = strlen(szStr);
	for(; (nLimit)--; szStr++) if(!IsHex(*szStr)) break;
	return nLimit <= 0;
}
inline int		ToUpper(int c){return toupper(c);};
inline int		ToLower(int c){return tolower(c);};
char		*	StringToUpper(char *szStr, int nLimit)
{
	if(!nLimit) nLimit = strlen(szStr);
	for(int i=0; i<nLimit; i++) if(IsLower(szStr[i])) szStr[i] = ToUpper(szStr[i]);
	return szStr;
}
char		*	StringToLower(char *szStr, int nLimit)
{
	if(!nLimit) nLimit = strlen(szStr);
	for(int i=0; i<nLimit; i++) if(IsUpper(szStr[i])) szStr[i] = ToLower(szStr[i]);
	return szStr;
}
int				StringToInt(const char *szStr)
{
	return strtoul(szStr, NULL, 0);
}
int				StringToInt(const char *szStr, char *&pStr)
{
	return strtoul(szStr, &pStr, 0);
}
int				StringToHex(const char *szStr)
{
	return strtoul(szStr, NULL, 16);
}
int				StringToHex(const char *szStr, char *&pStr)
{
	return strtoul(szStr, &pStr, 16);
}
double			StringToFloat(const char *szStr)
{
	return atof(szStr);
}
double			StringToFloat(const char *szStr, char *&pStr)
{
	return strtod(szStr, &pStr);
}
void			ReverseString(char *begin, char *end)
{
    char a;
    while(end > begin) a = *end, *end-- = *begin, *begin++ = a;
}
int				strchrcount(const char *szStr, int c)
{
	int r;
	for(r = 0; *szStr; szStr++) if(*szStr == c) r++;
	return r;
}
template<typename T>
T strrnthchr(T szStr, int c, int n)
{
	// yeah, strrnthchr...
	static T ptrs[100];
	if(!n) return strrchr(szStr, c);
	for(int i = 0; ; ++szStr)
	{
		if(!*szStr)
		{
			--i;
			if(i >= n) return ptrs[i-n];
			break;
		}
		if(*szStr == c) ptrs[i++] = szStr;
	}
	return nullptr;
}
template const char * strrnthchr<const char *>(const char *, int, int);
template char * strrnthchr<char *>(char *, int, int);
char *	szncpy(char *out, const char *src, size_t size)
{
	strncpy(out, src, size);
	out[size-1] = 0;
	return out;
}
char *	trim(char *szStr, const char * szChars)
{
	return rtrim(ltrim(szStr, szChars), szChars);
}
char *	ltrim(char *szStr, const char * szChars)
{
	for(;*szStr;szStr++) if(!strchr(szChars, *szStr)) break;
	return szStr;
}
char *	rtrim(char *szStr, const char * szChars)
{
	for(char *r = szStr + strlen(szStr); r>=szStr; r--)
	{
		if(strchr(szChars, *r)) *r = 0;
		else break;
	}
	return szStr;
}
const char *	ltrim(const char *szStr, const char * szChars)
{
	for(;*szStr;szStr++) if(!strchr(szChars, *szStr)) break;
	return szStr;
}

void Alert(const char *szStr)
{
	MessageBox(0, szStr, "Alert", 0);
}
void Print(const char *szStr)
{
	printf(">> %s\n", szStr);
}
void Output(const char *szStr)
{
	puts(szStr);
}
void Error(const char *szStr)
{
	Printf("ERROR: %s", szStr);
	g_nErrorStackCount = 0;
}
void Trace(const char *szStr)
{
#ifdef DEBUGIT
	printf("%s", szStr);
#endif
}
void Exception(const char szStr[256])
{
	char msg[512];
	strcpy(msg, "FATAL ERROR: ");
	strcat(msg, szStr);
	Print(msg);
	g_bExceptionMade = true;
}